package com.carey.blog.framework.latke.service;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;


import com.carey.blog.framework.latke.FwkStatusCodes;
import com.carey.blog.framework.latke.Keys;
import com.carey.blog.framework.latke.Latkes;
import com.carey.blog.framework.latke.model.Label;

/**
 * Language service.
 * 
 */
public final class LangPropsService {

	/**
	 * Logger.
	 */
	private static final Logger LOGGER = Logger
			.getLogger(LangPropsService.class.getName());
	/**
	 * Language properties.
	 */
	private static final Map<Locale, Map<String, String>> LANGS = new HashMap<Locale, Map<String, String>>();

	/**
	 * Gets all language properties as a map by the specified locale.
	 * 
	 * @param locale
	 *            the specified locale
	 * @return a map of language configurations
	 * @throws ServiceException
	 *             service exception
	 */
	public Map<String, String> getAll(final Locale locale)
			throws ServiceException {
		Map<String, String> ret = LANGS.get(locale);

		if (null == ret) {
			ret = new HashMap<String, String>();
			ResourceBundle langBundle = null;
			try {

				langBundle = ResourceBundle.getBundle(Keys.LANGUAGE, locale);
			} catch (final MissingResourceException e) {
				LOGGER.log(Level.WARNING,
						"{0}, using default locale[{1}] instead", new Object[] {
								e.getMessage(), Latkes.getDefaultLocale() });

				try {
					langBundle = ResourceBundle.getBundle(Keys.LANGUAGE,
							Latkes.getDefaultLocale());
				} catch (final MissingResourceException ex) {
					LOGGER.log(Level.WARNING,
							"{0}, using default lang.properties instead",
							new Object[] { e.getMessage() });
					langBundle = ResourceBundle.getBundle(Keys.LANGUAGE);
				}

			}

			final Enumeration<String> keys = langBundle.getKeys();
			while (keys.hasMoreElements()) {
				final String key = keys.nextElement();
				final String value = langBundle.getString(key);
				ret.put(key, value);
			}

			LANGS.put(locale, ret);
		}

		return ret;
	}

	/**
	 * Gets all language properties as labels from lang_(by the specified
	 * locale).properties file. If not found lang_(locale).properties
	 * configurations, using {@link Latkes#getDefaultLocale()} instead.
	 * 
	 * @param locale
	 *            the specified locale
	 * @return for example,
	 * 
	 *         <pre>
	 * {
	 *     "sc": "CHANGE_LOCALE_FAIL_NOT_FOUND",
	 *     "msgs": {
	 *         "localeNotFound":
	 *             "Unsupported locale, using default locale(zh_CN) instead."
	 *     },
	 *     "labels": [
	 *         {"labelId": "projectName", "labelText": "Carey Blog Web"},
	 *         ....
	 *     ]
	 * }
	 * </pre>
	 * @throws ServiceException
	 *             service exception
	 */
	public JSONObject getLabels(final Locale locale) throws ServiceException {
		final JSONObject ret = new JSONObject();
		ResourceBundle langBundle = null;

		try {
			langBundle = ResourceBundle.getBundle(Keys.LANGUAGE, locale);
		} catch (final MissingResourceException e) {
			LOGGER.log(Level.WARNING,
					"{0}, using default locale[{1}]  instead",
					new Object[] { e.getMessage(), Latkes.getDefaultLocale() });

			langBundle = ResourceBundle.getBundle(Keys.LANGUAGE,
					Latkes.getDefaultLocale());
		}

		final Enumeration<String> keys = langBundle.getKeys();
		final JSONArray labels = new JSONArray();

		try {
			ret.put(Label.LABELS, labels);

			while (keys.hasMoreElements()) {
				final JSONObject label = new JSONObject();
				final String key = keys.nextElement();
				label.put(Label.LABEL_ID, key);
				label.put(Label.LABEL_TEXT, langBundle.getString(key));

				labels.put(label);
			}

			ret.put(Keys.STATUS_CODE, FwkStatusCodes.CHANGE_LOCALE_SUCC);
		} catch (final JSONException e) {
			LOGGER.log(Level.SEVERE, e.getMessage(), e);

			throw new ServiceException(e);
		}

		return ret;
	}

	/**
	 * Gets a value from baseName_locale.properties file with the specified key.
	 * If not found baseName_(locale).properties configurations, using
	 * {@link Latkes#getDefaultLocale()} instead.
	 * 
	 * @param baseName
	 *            base name of resource bundle, options as the following:
	 *            <ul>
	 *            <li>{@link Keys#LANGUAGE}</li>
	 *            </ul>
	 * @param key
	 *            the specified key
	 * @param locale
	 *            the specified locale
	 * @return the value of the specified key
	 */
	public String get(final String baseName, final String key,
			final Locale locale) {
		if (!Keys.LANGUAGE.equals(baseName)) {
			final RuntimeException e = new RuntimeException(
					"i18n resource[baseName=" + baseName + "] not found");
			LOGGER.log(Level.SEVERE, e.getMessage(), e);

			throw e;
		}

		try {
			return ResourceBundle.getBundle(baseName, locale).getString(key);
		} catch (final MissingResourceException e) {
			LOGGER.log(Level.WARNING, "{0}, get it from default locale[{1}]",
					new Object[] { e.getMessage(), Latkes.getDefaultLocale() });

			return ResourceBundle
					.getBundle(baseName, Latkes.getDefaultLocale()).getString(
							key);
		}
	}

	/**
	 * Gets the {@link LangPropsService} singleton.
	 * 
	 * @return the singleton
	 */
	public static LangPropsService getInstance() {
		return SingletonHolder.SINGLETON;
	}

	/**
	 * Private default constructor.
	 */
	private LangPropsService() {
	}

	/**
	 * Singleton holder.
	 * 
	 */
	private static final class SingletonHolder {

		/**
		 * Singleton.
		 */
		private static final LangPropsService SINGLETON = new LangPropsService();

		/**
		 * Private default constructor.
		 */
		private SingletonHolder() {
		}
	}
}
